import {Formula} from "./FormulaUtil.js";
import FormConstant from "../../../../../js/FormConstant";
import loopControl from "./loopControl.js";
import Util from "@/js/Util.js"
/**
 * @type {{calcAreas(*, *, *): void, calc: FormCalc.calc}}
 */
const FormCalc = {

    /**
     * {
     *  //表名:{}
     *   main:{key:{公式}},
     *   contract:{key:公式}
     * }
     * @param formulas
     * @param formData
     * @param formConfig
     */
    calc: function (formulas, formData,formConfig) {
        //构建数据数组。
        let aryData = this.handData(formData);
        let data = this.getData(aryData);
        //计算一对多子表
        for (let key in formulas) {
            let type = this.getTypeByTableName(key, aryData);
            if (type == "main" || type == "oneTable") {
                continue;
            }
            let subFormulas = formulas[key];
            //处理一对多子表的公式。
            this.handOneToMany(key, subFormulas, data, aryData,formConfig);
        }
        //计算计算主表和一对一子表。
        for (let table in formulas) {
            let type = this.getTypeByTableName(table, aryData);
            if (type == "subTable") {
                continue;
            }
            let formulaObj = formulas[table];
            this.handMainOneToOne(table, formulaObj, data, aryData,formConfig)
        }
    },
    /**
     * 将数据构建成如下的格式
     * [{table"",type:"onetoone",data:[]}]
     * @param {Object} formData     表单数据
     * @returns {[]}
     */
    handData(formData) {
        //构建数据数组。
        let aryData = [];
        let mainData = {table: "main", type: "main", data: formData};
        aryData.push(mainData);
        //处理子表。
        for (let key in formData) {
            if (key.startsWith(FormConstant.SubPrefix)) {
                let data = formData[key];
                let table = key.replace(FormConstant.SubPrefix, "");
                let type = (data instanceof Array) ? "subTable" : "oneTable";

                let tableJson = {table: table, type: type, data};

                aryData.push(tableJson);
            }
        }
        return aryData;
    },
    /**
     * 将数据构建成如下的格式
     * {main: [], subTableName:[]}
     * @param aryData
     * @returns {{}}
     */
    getData(aryData) {
        let data = {};
        for (let i = 0; i < aryData.length; i++) {
            let o = aryData[i];
            data[o.table] = o.data;
        }
        return data;
    },
    /**
     * 根据表名获取类型
     * @param tableName
     * @param aryData
     * @returns {null|*}
     */
    getTypeByTableName(tableName, aryData) {
        for (let i = 0; i < aryData.length; i++) {
            let row = aryData[i];
            if (row.table == tableName) {
                return row.type;
            }
        }
        return null;
    },
    /**
     * 根据表名获取数据
     * @param tableName
     * @param aryData
     * @returns {null|*}
     */
    getDataByTableName(tableName, aryData) {
        for (let i = 0; i < aryData.length; i++) {
            let row = aryData[i];
            if (row.table == tableName) {
                return row.data;
            }
        }
        return null;
    },
    /**
     * 处理一对多子表。
     * <pre>
     *   1.替换主表的公式字段
     *   2.替换一对一子表的公式字段
     *   3.替换一对多子表字段。
     *  </pre>
     * @param key
     * @param data
     * @param aryData
     * @param formConfig
     */
    handOneToMany(table, formulas, data, aryData,formConfig) {
        let subData = this.getDataByTableName(table, aryData);
        for (let rowField in formulas) {
            let formula = formulas[rowField].calc;
            let condition = formulas[rowField].condition;
            formula = this.handRowFormula(formula, aryData).formula;
            for (let i = 0, j = subData.length; i < j; i++) {
                let row = subData[i];
                if(!formula || !this.executeCondition(condition,data,aryData)){
                    continue;
                }
                let val = this.getValue(formula,this,row,data);
                val = this.handException(val);
                let config = this.getConfigByField(table,rowField,formConfig);
                //数字控件
                if(config.controlConfig.type == "number"){
                    val = Util.roundToPrecision(val,config.controlConfig.options.decimalLength || 0)
                }else {
                    val = val + '';
                }
                row[rowField] = val;
            }
        }
    },
    /**
     * 替换子表的公式。
     * @param formula
     * @param aryData
     * @returns {{formula: *, table: *}}
     */
    handRowFormula(formula, aryData) {
        if(!formula){
            return "";
        }
        let curTable = "";
        //主表替换正则  支持子表公式中有主表的字段
        // {(中文)field}
        // {field}
        let mainReg = /\{(\(.*?\)){0,1}(\w*?)\}/g;
        let formula_ = formula.replace(mainReg, function (a, b, c) {
            return "data.main." + c;
        });
        //处理一对一的子表
        //获取公式的子表，如果子表属于一对一的子表则进行替换。
        let oneReg = /\{(\(.*?\)){0,1}(\w*?)\.(\w*?)\}/g;
        let match = oneReg.exec(formula);
        while (match != null) {
            let table = match[2];
            let field = match[3];
            let type = this.getTypeByTableName(table, aryData);
            if (type == "onetoone") {
                formula_ = formula_.replace(match[0], function () {
                    return "data." + table + "." + field;
                });
            }
            //是一对多的情况。
            else {
                if (!curTable) {
                    curTable = table;
                }
            }
            match = oneReg.exec(formula_);
        }
        //子表表替换正则
        // 匹配 {(中文)table.field}
        // 匹配 {table.field}
        let rowReg = /\{(\(.*?\)){0,1}(\w*?)\.(\w*?)\}/g;
        formula_ = formula_.replace(rowReg, function (a, b, c, d) {
            return "row." + d + "";
        });
        return {formula: formula_, table: curTable};
    },

    /**
     * 处理主表，一对一的公式。
     * <pre>
     * <pre>
     * @param table     公式所在的表
     * @param formulas  formulas 公式
     * @param data      data {表:数据}
     * @param aryData   [{table:"",type:"",data:[]}]
     * @param formConfig   表单配置
     */
    handMainOneToOne(table, formulas, data, aryData,formConfig) {
        for (let field in formulas) {
            //公式。
            let formula = formulas[field].calc;
            let condition = formulas[field].condition;
            if( !formula || !this.executeCondition(condition,data,aryData)){
                continue;
            }
            let val = this.calcFormula(formula, data, aryData);
            val = this.handException(val);
            let config = this.getConfigByField(table,field,formConfig);
            //数字控件
            if(config.type == "number"){
                val = Util.roundToPrecision(val,config.options.decimalLength || 0)
            }else {
                val = val + '';
            }
            data[table][field] = val;
        }
    },
    /**
     * 处理当被除数为0的时候，返回空字符串。
     * @param val
     * @returns {string|*}
     */
    handException(val){
        let isNUm=true;
        //判断是否为字符串数据
        if(typeof val == 'string'){
            const unixTimestampPattern = /^\d+$/;
            isNUm = unixTimestampPattern.test(val);
        }
        if(val== "Infinity" || ( isNUm && isNaN(val))){
            return  "";
        }
        return val;
    },
    calcFormula(formula, data, aryData) {
        let arrayData = [...aryData];
        //1. 计算子表集合。
        let regStr = /\[(.*?)\]/g;
        //没有子表。
        let match = regStr.exec(formula);
        let m = 1;
        while (match != null) {
            let subFormula = match[1];
            let subAry = "subAry_" + m;
            //{formula:"公式",table:""}
            let rowFormulaObj = this.handRowFormula(subFormula, arrayData);
            let subTable = rowFormulaObj.table;
            let rowFormula = rowFormulaObj.formula;
            //子表数据
            let subData = data[subTable];
            //子表数组。
            let aryRows = [];
            for (let i = 0, len = subData.length; i < len; i++) {
                //取出一行数据进行计算。
                let row = subData[i];
                let rowData = this.getValue(rowFormula,this,row,data);
                aryRows.push(rowData);
            }
            data[subAry] = aryRows;
            formula = formula.replace(match[0], "data." + subAry);
            m++;

            match = regStr.exec(regStr);
        }
        //2.替换主表
        let mainReg = /\{(\(.*?\)){0,1}(\w*?)\}/g;
        formula = formula.replace(mainReg, function (a, b, c) {
            return "data.main." + c;
        });
        //3.替换子表
        let rowReg = /\{(\(.*?\)){0,1}(\w*?)\.(\w*?)\}/g;
        formula = formula.replace(rowReg, function (a, b, c, d) {
            return "data."+ c + "." + d;
        });
        let val = this.getValue(formula,this,data,data);
        return val;
    },
    getValue(calc,self,row,data){
        if(!calc.includes('return')){
            calc =`return ${calc}`
        }
        let fn = new Function('row','data','Formula',calc);
        return  fn.call(self,row,data,Formula)
    },
    /**
     *执行条件脚本
     * @param condition 条件
     * @param data  表单数据
     * @param row   子表当前行
     * @returns {*}
     */
    executeCondition(condition,data,aryData){
        if(!condition){
            return true;
        }
        return this.calcFormula(condition, data, aryData) || false;
    },
    /**
     * 根据field获取当前控件配置
     * @param table
     * @param field
     * @param formConfig
     */
    getConfigByField(table, field, formConfig){
        let config = "";
        loopControl(formConfig, (item, type) => {
            if ((type=="subTable" || type=="oneTable") && item.field == table) {
                config = this.getConfigByField(item.field,field,item.children);
                if(config){
                    return true;
                }
            }  else {
                if(item.field == field){
                    config = item;
                    return true;
                }
            }
        })
        return config;
    }
}

export default FormCalc;
